package gobitfield

import (
	"encoding/binary"
	"fmt"
	"math"
	"reflect"
	"strconv"
	"strings"
	"sync"
)

func Marshal(iface interface{}) (bytes []byte, e error) {
	const (
		functionName = "Marshal"
	)

	return
}

func Unmarshal(bytes []byte, iface interface{}) (e error) {
	const (
		functionName = "Unmarshal"
	)

	return
}

// Standard library features

const (
	bitfieldTagKey         = "bitfield"
	bitfieldTagValueFormat = "%s,%d"
)

func checkInput(data any) error {
	if data == nil {
		return fmt.Errorf("data cannot be nil")
	}
	reflection := reflect.TypeOf(data)
	if reflection.Kind() == reflect.Pointer {
		if reflection.Elem().Kind() == reflect.Struct {
			return nil
		}
	}
	return fmt.Errorf("data must be a pointer to a struct")
}

func Decode(bytes []byte, st any) error {

	err := checkInput(st)
	if err != nil {
		return err
	}

	v := reflect.ValueOf(st).Elem()

	_, ok := structMeta.Load(v.Type())
	if !ok {
		err := updateStructMeta(v)
		if err != nil {
			return err
		}
	}

	d := &decoder{order: binary.BigEndian, buf: bytes}
	d.value(v)

	return nil
}

func Encode(st any) (bytes []byte, err error) {

	err = checkInput(st)
	if err != nil {
		return nil, err
	}

	v := reflect.Indirect(reflect.ValueOf(st))
	_, ok := structMeta.Load(v.Type())
	if !ok {
		err := updateStructMeta(v)
		if err != nil {
			return nil, err
		}
	}

	buf := make([]byte, 8192)
	e := &encoder{order: binary.BigEndian, buf: buf}
	e.value(v)

	return buf[:e.offset], nil
}

type coder struct {
	order  binary.ByteOrder
	buf    []byte
	offset int
}

type decoder coder
type encoder coder

func (d *decoder) validateBuffer(n int) {
	if d.offset+n > len(d.buf) {
		panic("buffer overflow")
	}
}

func (e *encoder) validateBuffer(n int) {
	if e.offset+n > len(e.buf) {
		e.buf = append(e.buf, make([]byte, 64)...)
	}
}

func (d *decoder) bool() bool {
	d.validateBuffer(1)
	x := d.buf[d.offset]
	d.offset++
	return x != 0
}

func (e *encoder) bool(x bool) {
	e.validateBuffer(1)
	if x {
		e.buf[e.offset] = 1
	} else {
		e.buf[e.offset] = 0
	}
	e.offset++
}

func (d *decoder) uint8() uint8 {
	d.validateBuffer(1)
	x := d.buf[d.offset]
	d.offset++
	return x
}

func (e *encoder) uint8(x uint8) {
	e.validateBuffer(1)
	e.buf[e.offset] = x
	e.offset++
}

func (d *decoder) uint16() uint16 {
	d.validateBuffer(2)
	x := d.order.Uint16(d.buf[d.offset : d.offset+2])
	d.offset += 2
	return x
}

func (e *encoder) uint16(x uint16) {
	e.validateBuffer(2)
	e.order.PutUint16(e.buf[e.offset:e.offset+2], x)
	e.offset += 2
}

func (d *decoder) uint32() uint32 {
	d.validateBuffer(4)
	x := d.order.Uint32(d.buf[d.offset : d.offset+4])
	d.offset += 4
	return x
}

func (e *encoder) uint32(x uint32) {
	e.validateBuffer(4)
	e.order.PutUint32(e.buf[e.offset:e.offset+4], x)
	e.offset += 4
}

func (d *decoder) uint64() uint64 {
	d.validateBuffer(8)
	x := d.order.Uint64(d.buf[d.offset : d.offset+8])
	d.offset += 8
	return x
}

func (e *encoder) uint64(x uint64) {
	e.validateBuffer(8)
	e.order.PutUint64(e.buf[e.offset:e.offset+8], x)
	e.offset += 8
}

func (d *decoder) int8() int8 { return int8(d.uint8()) }

func (e *encoder) int8(x int8) { e.uint8(uint8(x)) }

func (d *decoder) int16() int16 { return int16(d.uint16()) }

func (e *encoder) int16(x int16) { e.uint16(uint16(x)) }

func (d *decoder) int32() int32 { return int32(d.uint32()) }

func (e *encoder) int32(x int32) { e.uint32(uint32(x)) }

func (d *decoder) int64() int64 { return int64(d.uint64()) }

func (e *encoder) int64(x int64) { e.uint64(uint64(x)) }

func (d *decoder) value(v reflect.Value) {
	switch v.Kind() {
	case reflect.Array:
		l := v.Len()
		for i := 0; i < l; i++ {
			d.value(v.Index(i))
		}

	case reflect.Struct:
		t := v.Type()
		var stMeta *StructMeta
		mapVal, ok := structMeta.Load(t)
		if ok {
			stMeta = mapVal.(*StructMeta)
		}
		bitFieldCellIdx := 0
		bitFieldCellBitLen := 0
		bitFieldIdx := 0
		bitFieldCellValue := interface{}(nil)
		for i, n := 0, v.NumField(); i < n; i++ {
			tag := t.Field(i).Tag.Get(bitfieldTagKey)
			if tag == "" {
				d.value(v.Field(i))
			} else {
				kind := stMeta.BitFieldCells[bitFieldCellIdx].Kind
				if bitFieldIdx == 0 {
					if kind == reflect.Uint8 {
						bitFieldCellValue = d.uint8()
					} else if kind == reflect.Uint16 {
						bitFieldCellValue = d.uint16()
					} else if kind == reflect.Uint32 {
						bitFieldCellValue = d.uint32()
					} else if kind == reflect.Uint64 {
						bitFieldCellValue = d.uint64()
					}
				}
				bitLen := stMeta.BitFieldCells[bitFieldCellIdx].Fields[bitFieldIdx].BitLen
				bitOffset := stMeta.BitFieldCells[bitFieldCellIdx].Fields[bitFieldIdx].BitOffset
				if kind == reflect.Uint8 {
					bitFieldValue := uint8(bitFieldCellValue.(uint8)>>bitOffset) & (1<<bitLen - 1)
					v.Field(i).SetUint(uint64(bitFieldValue))
				} else if kind == reflect.Uint16 {
					bitFieldValue := uint16(bitFieldCellValue.(uint16)>>bitOffset) & (1<<bitLen - 1)
					v.Field(i).SetUint(uint64(bitFieldValue))
				} else if kind == reflect.Uint32 {
					bitFieldValue := uint32(bitFieldCellValue.(uint32)>>bitOffset) & (1<<bitLen - 1)
					v.Field(i).SetUint(uint64(bitFieldValue))
				} else if kind == reflect.Uint64 {
					bitFieldValue := uint64(bitFieldCellValue.(uint64)>>bitOffset) & (1<<bitLen - 1)
					v.Field(i).SetUint(bitFieldValue)
				}
				bitFieldIdx++

				bitFieldCellBitLen += bitLen
				if bitFieldCellBitLen == stMeta.BitFieldCells[bitFieldCellIdx].BitLen {
					bitFieldCellBitLen = 0
					bitFieldIdx = 0
					bitFieldCellIdx++
				}
			}
		}

	case reflect.Slice:
		l := v.Len()
		for i := 0; i < l; i++ {
			d.value(v.Index(i))
		}

	case reflect.Bool:
		v.SetBool(d.bool())

	case reflect.Int8:
		v.SetInt(int64(d.int8()))
	case reflect.Int16:
		v.SetInt(int64(d.int16()))
	case reflect.Int32:
		v.SetInt(int64(d.int32()))
	case reflect.Int64:
		v.SetInt(d.int64())

	case reflect.Uint8:
		v.SetUint(uint64(d.uint8()))
	case reflect.Uint16:
		v.SetUint(uint64(d.uint16()))
	case reflect.Uint32:
		v.SetUint(uint64(d.uint32()))
	case reflect.Uint64:
		v.SetUint(d.uint64())

	case reflect.Float32:
		v.SetFloat(float64(math.Float32frombits(d.uint32())))
	case reflect.Float64:
		v.SetFloat(math.Float64frombits(d.uint64()))

	case reflect.Complex64:
		v.SetComplex(complex(
			float64(math.Float32frombits(d.uint32())),
			float64(math.Float32frombits(d.uint32())),
		))
	case reflect.Complex128:
		v.SetComplex(complex(
			math.Float64frombits(d.uint64()),
			math.Float64frombits(d.uint64()),
		))
	}
}

func (e *encoder) value(v reflect.Value) {
	switch v.Kind() {
	case reflect.Array:
		l := v.Len()
		for i := 0; i < l; i++ {
			e.value(v.Index(i))
		}

	case reflect.Struct:
		t := v.Type()
		var stMeta *StructMeta
		mapVal, ok := structMeta.Load(t)
		if ok {
			stMeta = mapVal.(*StructMeta)
		}
		bitFieldCellIdx := 0
		bitFieldCellBitLen := 0
		bitFieldIdx := 0
		bitFieldCellValue := interface{}(nil)
		for i, n := 0, v.NumField(); i < n; i++ {
			tag := t.Field(i).Tag.Get(bitfieldTagKey)
			if tag == "" {
				e.value(v.Field(i))
			} else {
				kind := stMeta.BitFieldCells[bitFieldCellIdx].Kind
				if bitFieldIdx == 0 {
					if kind == reflect.Uint8 {
						bitFieldCellValue = uint8(0)
					} else if kind == reflect.Uint16 {
						bitFieldCellValue = uint16(0)
					} else if kind == reflect.Uint32 {
						bitFieldCellValue = uint32(0)
					} else if kind == reflect.Uint64 {
						bitFieldCellValue = uint64(0)
					}
				}
				bitLen := stMeta.BitFieldCells[bitFieldCellIdx].Fields[bitFieldIdx].BitLen
				bitOffset := stMeta.BitFieldCells[bitFieldCellIdx].Fields[bitFieldIdx].BitOffset
				if kind == reflect.Uint8 {
					bitFieldValue := uint8(v.Field(i).Uint())
					bitFieldCellValue = bitFieldCellValue.(uint8) | (bitFieldValue << bitOffset)
				} else if kind == reflect.Uint16 {
					bitFieldValue := uint16(v.Field(i).Uint())
					bitFieldCellValue = bitFieldCellValue.(uint16) | (bitFieldValue << bitOffset)
				} else if kind == reflect.Uint32 {
					bitFieldValue := uint32(v.Field(i).Uint())
					bitFieldCellValue = bitFieldCellValue.(uint32) | (bitFieldValue << bitOffset)
				} else if kind == reflect.Uint64 {
					bitFieldValue := uint64(v.Field(i).Uint())
					bitFieldCellValue = bitFieldCellValue.(uint64) | (bitFieldValue << bitOffset)
				}
				bitFieldIdx++

				bitFieldCellBitLen += bitLen
				if bitFieldCellBitLen == stMeta.BitFieldCells[bitFieldCellIdx].BitLen {
					if kind == reflect.Uint8 {
						e.uint8(bitFieldCellValue.(uint8))
					} else if kind == reflect.Uint16 {
						e.uint16(bitFieldCellValue.(uint16))
					} else if kind == reflect.Uint32 {
						e.uint32(bitFieldCellValue.(uint32))
					} else if kind == reflect.Uint64 {
						e.uint64(bitFieldCellValue.(uint64))
					}
					bitFieldCellBitLen = 0
					bitFieldIdx = 0
					bitFieldCellIdx++
				}
			}
		}

	case reflect.Slice:
		l := v.Len()
		for i := 0; i < l; i++ {
			e.value(v.Index(i))
		}

	case reflect.Bool:
		e.bool(v.Bool())

	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		switch v.Type().Kind() {
		case reflect.Int8:
			e.int8(int8(v.Int()))
		case reflect.Int16:
			e.int16(int16(v.Int()))
		case reflect.Int32:
			e.int32(int32(v.Int()))
		case reflect.Int64:
			e.int64(v.Int())
		}

	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		switch v.Type().Kind() {
		case reflect.Uint8:
			e.uint8(uint8(v.Uint()))
		case reflect.Uint16:
			e.uint16(uint16(v.Uint()))
		case reflect.Uint32:
			e.uint32(uint32(v.Uint()))
		case reflect.Uint64:
			e.uint64(v.Uint())
		}

	case reflect.Float32, reflect.Float64:
		switch v.Type().Kind() {
		case reflect.Float32:
			e.uint32(math.Float32bits(float32(v.Float())))
		case reflect.Float64:
			e.uint64(math.Float64bits(v.Float()))
		}

	case reflect.Complex64, reflect.Complex128:
		switch v.Type().Kind() {
		case reflect.Complex64:
			x := v.Complex()
			e.uint32(math.Float32bits(float32(real(x))))
			e.uint32(math.Float32bits(float32(imag(x))))
		case reflect.Complex128:
			x := v.Complex()
			e.uint64(math.Float64bits(real(x)))
			e.uint64(math.Float64bits(imag(x)))
		}
	}
}

type BitFieldMeta struct {
	Name      string
	BitLen    int
	BitOffset int
}

type BitFieldCell struct {
	Kind   reflect.Kind // 只支持reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64
	BitLen int
	Fields []BitFieldMeta
}

type StructMeta struct {
	Name          string
	BitFieldCells []BitFieldCell
}

var structMeta sync.Map // map[reflect.Type]*StructMeta

func updateStructMeta(v reflect.Value) error {
	if v.Kind() == reflect.Pointer && v.Elem().Kind() == reflect.Struct {
		v = v.Elem()
	}
	if v.Kind() == reflect.Struct {
		t := v.Type()
		_, ok := structMeta.Load(t)
		if ok {
			return nil
		}
		var bitFieldCells []BitFieldCell
		var bitFieldCell BitFieldCell
		for i, n := 0, v.NumField(); i < n; i++ {
			tag, ok := t.Field(i).Tag.Lookup(bitfieldTagKey)
			if !ok {
				err := updateStructMeta(v.Field(i))
				if err != nil {
					return err
				}
			} else {
				kind := t.Field(i).Type.Kind()
				if err := checkBitFieldKind(kind); err != nil {
					return err
				}
				if len(bitFieldCell.Fields) > 0 {
					if bitFieldCell.Kind != kind {
						return fmt.Errorf("field %s tag %s kind not match", t.Field(i).Name, tag)
					}
				}

				bits := t.Field(i).Type.Bits()
				bitLen, err := strconv.Atoi(strings.Trim(tag, " "))
				if err != nil {
					return err
				}
				if bitLen <= 0 {
					return fmt.Errorf("field %s tag %s bitLen %d error", t.Field(i).Name, tag, bitLen)
				}
				if bitLen+bitFieldCell.BitLen > bits {
					return fmt.Errorf("field %s tag %s bitLen %d error", t.Field(i).Name, tag, bitLen)
				}

				var bitFieldMeta BitFieldMeta
				bitFieldMeta.Name = t.Field(i).Name
				bitFieldMeta.BitLen = bitLen
				bitFieldMeta.BitOffset = (bits - bitFieldCell.BitLen - bitLen)

				bitFieldCell.Kind = kind
				bitFieldCell.BitLen += bitLen
				bitFieldCell.Fields = append(bitFieldCell.Fields, bitFieldMeta)

				if bitFieldCell.BitLen == bits {
					bitFieldCells = append(bitFieldCells, bitFieldCell)
					bitFieldCell = BitFieldCell{}
				}
			}
		}
		if bitFieldCell.BitLen > 0 {
			return fmt.Errorf("BitLen %d can't have a remainder", bitFieldCell.BitLen)
		}
		stMeta := &StructMeta{
			Name:          t.Name(),
			BitFieldCells: bitFieldCells,
		}
		structMeta.Store(t, stMeta)
		return nil
	}

	return nil
}

func checkBitFieldKind(kind reflect.Kind) error {
	if kind != reflect.Uint8 && kind != reflect.Uint16 && kind != reflect.Uint32 && kind != reflect.Uint64 {
		return fmt.Errorf("checkBitFieldKind: kind %s not supported", kind)
	}
	return nil
}

func dumpStructMeta() {
	structMeta.Range(func(key, value interface{}) bool {
		stMeta := value.(*StructMeta)
		fmt.Printf("%s:\n", stMeta.Name)
		for _, bitFieldCell := range stMeta.BitFieldCells {
			fmt.Println("    bitFieldCell:", bitFieldCell.Kind, bitFieldCell.BitLen)
			for _, bitField := range bitFieldCell.Fields {
				fmt.Println("        bitField:", bitField.Name, bitField.BitLen, bitField.BitOffset)
			}
		}
		return true
	})
}
